home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Carousel
/
CAROUSEL.cdr
/
mactosh
/
utilprn
/
hpdeskje.sit
/
HPDJet ƒ
/
XPrint.c
< prev
Wrap
C/C++ Source or Header
|
1989-04-02
|
33KB
|
1,038 lines
/* 02.04.1989 amn (latest edit) */
/* XPrint.c - low-level printer driver for Macintosh and HP DeskJet. */
/* Compiles into 'DRVR' resource, id 2, name '.XPrint'. */
/* LightspeedC compiler produces also a 'DATA' resource, id -16320 */
/* which contains driver's global variables. A handle to these is placed */
/* into driver's device control entry in 'dCtlStorage' (IM II-190). */
/* These resources are placed into the printer resource file */
/* 'HP DeskJet', type 'PRER', creator 's89^' as */
/* 'DRVR' -8192 '.XPrint' and */
/* 'PREC' -8192. */
/* The 'Chooser' desk accessory changes the name of the active printer resource file */
/* in the 'STR ' -8192 in 'System' file. */
/* We suppose drivers shouldn't do 'SetResLoad(FALSE)'. */
/* The application currently running may have decided to do so. */
/* This is why we try to call 'LoadResource()' even directly after 'GetResource()'. */
/* LightspeedC driver header code seems to call 'SetResLoad(TRUE)' when processing */
/* an open call. We hope this doesn't interfere seriously with applications. */
/* Authors: Ari Mujunen (amn@hutcs.hut.fi) and Olli Arnberg (oar@hutcs.hut.fi). */
/* Copyright Ari Mujunen, Olli Arnberg 1989. */
/* You may redistribute the driver (=printer resource file, source files, */
/* documentation file(s), and the file 'Copyright and Source Offer') */
/* only _non-commercially_ and _in its entirety_. */
/* See the file 'Copyright and Source Offer' and/or documentation for details. */
/* Acknowledgements: Special thanks to Mr. Earle R. Horton for his 'Daisy' */
/* daisywheel printer driver and its source code published in 'MacTutor', Nov-Dec 1987. */
/* This driver served as a basis and inspiration for our work. It also */
/* proofed that a Macintosh printer driver can be done despite the lack of */
/* documentation from Apple. */
/* Change history: */
/* Version When Who Why */
/* 2.0 12.11.1988 amn Original rewrite. */
/* 13.11.1988 amn Getting our globals when we recognize they are not ours. */
/* 14.11.1988 amn Verifying why 'FKEY' 4 opens us _two_ 1st times. */
/* 15.11.1988 amn --"-- */
/* 16.11.1988 amn Found out that we killed Device Manager's dCtlFlags */
/* low-order byte by storing directly to it. */
/* Cleaning off 'asm's. */
/* 18.11.1988 amn Preparing to use fewer globals. */
/* 19.11.1988 amn Globals reduced, comments checked. */
/* 20.11.1988 amn Getting the volume of 'System' file. */
/* 21.11.1988 amn Names in 'prglobal.h' changed. */
/* 24.11.1988 amn Read printed listing... */
/* 25.11.1988 amn Checking the magic number in our globals correctly. */
/* 26.11.1988 amn Set 'dCtlFlags' every time we are opened. */
/* The actual error was an extraneous semicolon */
/* in the 'if' statement opening serial driver. */
/* 27.11.1988 amn Searching for a memory overwrite problem. */
/* Found it: used a global before they were read from resource. */
/* 12.12.1988 amn Grouping of consecutive empty bit lines to one DJ command. */
/* 03.02.1989 amn Moving RESID-constants to prglobals.h. */
/* 06.02.1989 amn/oar Dropping compression when in LaserJet compatibility mode. */
/* 25.02.1989 amn Searching why this still compresses while in LJC-mode. */
/* Don't know, now it definitely does not compress in LJC-mode. */
/* 24.03.1989 amn Adding detection of dirty and non-dirty bit lines in LJC-mode. */
/* 25.03.1989 amn/oar Removing global resource file refNums. */
/* Does not deallocate globals when closed: MF-compatibility. */
/* 26.03.1989 amn Reload 'Stng' and 'STR#' every time opened */
/* so Chooser changes take effect immediately. */
/* 28.03.1989 amn Settable printer origin. */
/* 2.1 02.04.1989 amn,oar Released version. */
#include "common_mac_includes.h"
/* Mac OS includes specific to this module: */
#include <ResourceMgr.h>
/* #include <Environs.h> we don't have this feature. */
#include <SerialDvr.h>
/* #include <HFS.h>: (needs FileMgr.h); we don't have this feature. */
#define DRIVER_MODULE
#include "prglobals.h"
/* Device driver routine numbers. LightspeedC passes this number as a routine */
/* selector value in the third integer parameter to the function 'main'. */
#define OPEN 0
#define PRIME 1
#define CONTROL 2
#define STATUS 3
#define CLOSE 4
/* Control command codes (csCode) for the serial driver. */
#define SERIAL_RESET 8
#define SERIAL_HANDSHAKE 10
/* Xon/Xoff characters to be used when handshaking is requested from the serial driver. */
#define XON_CHAR ((char)17)
#define XOFF_CHAR ((char)19)
/* Font characterization table for use by FontManager and XPrint. */
typedef struct {
int vres;
int hres;
SignedByte bold[3];
SignedByte italic[3];
SignedByte notused[3];
SignedByte outline[3];
SignedByte shadow[3];
SignedByte condensed[3];
SignedByte extended[3];
SignedByte underline[3];
} tFontCharacterizationTable, *ptFontCharacterizationTable, **htFontCharacterizationTable;
/* Function prototypes for every function in this source file.*/
/* Device driver entry point. LightspeedC generates code to call this function. */
int main(ptPrParam, DCtlPtr, int);
/* Routine which opens our device driver. Get our global variables */
/* as a resource 'PREC', id -8192, load them into memory and lock them down. */
/* Load settings resource 'Stng', id -4080 into global struct 'currentSettings'. */
OSErr driverOpen(DCtlPtr);
/* Open ourselves (=printer resource file) and */
/* return the refNum of our printer resource file. */
int openPrinterResourceFile(DCtlPtr);
/* Load 'Sntg' and 'STR#' settings into globals. */
OSErr reloadSettings(void);
/* Low-level driver must be capable of sending printer controls as FF, LF etc. in */
/* a device-independent manner. These routines fetch the actual control strings from */
/* settings resource 'STR#', id -4080. */
void setControlStrings(StringHandle);
void copyPascalString(StringPtr, StringPtr);
void decodeCaretsToControlCharacters(StringPtr);
/* Actual printing is done using Mac serial driver called '.AOut'/'.BOut'. */
/* The driver is never closed, because the old 64K ROM serial driver hangs the */
/* machine when closed. Hardware (CTS) handshaking is strongly recommended. */
OSErr openSerialDriverAndConfigureIt(void);
OSErr openDriverByName(StringPtr);
void reconfigureSerialPort(void);
/* Routines that send characters to the serial line and the printer. */
OSErr sendPrinterControl(long);
OSErr sendString(StringPtr);
/* Routines that compress and send a bitmap to the printer. */
OSErr printBitMap(int, BitMap, Rect);
void compressBitLine(int, StringPtr, int *, StringPtr, Boolean *);
/* Function definitions. */
int
main(
pb, /* ==> parameter block with which we were called*/
dce, /* ==> device control entry */
routineSelector /* which of OPEN, PRIME, CONTROL, STATUS, CLOSE */
)
ptPrParam pb;
DCtlPtr dce;
int routineSelector;
{
/* Check to make sure our data area was allocated. We cannot image how this code */
/* can be in memory unless the OPEN routine is called first. */
/* Note that we cannot use any of LightspeedC's "global" variables */
/* until after we get our storage from the resource file and lock it down, */
/* making A4 point to it. This probably does the same thing that the prolog code */
/* attempts to do with the original 'DATA' resource created by LightspeedC. */
OSErr retCode;
if (!((routineSelector == OPEN) || (routineSelector == CLOSE))) {
if (dce->dCtlStorage == nil)
SysError(25); /* somebody managed to call CONTROL/STATUS before OPEN */
if ((*(long *)(*dce->dCtlStorage)) != OUR_MAGIC_NUMBER)
SysError(25); /* it is not ours */
/* Now we have our globals (dce->dCtlStorage), ensure lockedness: */
HLock(dce->dCtlStorage);
/* Make LSC use the locked storage as its globals: */
{
Ptr pointer;
/* Strip off Memory Manager bits. */
pointer = (Ptr)((long)(*(dce->dCtlStorage)) & (Lo3Bytes));
asm {
movea.l pointer,a4 ;; LSC uses A4 as the base for driver globals
}
}
} /* end if other than CLOSE */
switch (routineSelector) { /* dispatch */
case OPEN:
/* Applications seem to call this often to ensure our driver is open. */
/* 'FKEY' 4, screen print, opens us after it has noticed that the first */
/* screen dump call failed because the driver isn't open (and not in memory). */
/* The Device Manager of System 2.0 calls us despite we are open. */
/* System 4.2 does not seem to call any longer. */
if (dce->dCtlStorage != nil)
if ((*(long *)(*dce->dCtlStorage)) != OUR_MAGIC_NUMBER) {
DisposHandle(dce->dCtlStorage); /* is is not ours, dispose it */
dce->dCtlStorage = nil;
}
if (dce->dCtlStorage == nil) {
if ((retCode = driverOpen(dce)) != noErr) {
PrintErr = retCode;
(void)CloseDriver(dce->dCtlRefNum); /* calls recursively ??? */
return(retCode);
}
}
/* If we are reopened by Chooser, the (possibly) changed settings */
/* must be reloaded. */
if ((retCode = reloadSettings()) != noErr)
return(retCode);
reconfigureSerialPort();
dce->dCtlFlags = dCtlEnable | dStatEnable | (dce->dCtlFlags & 0x00FF);
/* Locking is not requested, BUT just in case high-level printing code */
/* manages to get memory by purging our driver doesn't seem fair... */
/* Perhaps the resource bit 'Purgeable' should be cleared ??? */
/* Printer driver version. */
dce->dCtlQHdr.qFlags = PRINTING_MANAGER_VERSION; /* Yes, it seems to be there !!! */
PrintErr = noErr; /* Somebody may bother to check this. */
break; /* case OPEN */
case PRIME:
/* Read/write calls not for low-level printer drivers. */
break;
case CONTROL:
switch (pb->csCode) {
/* 'pb->csCode' gives opcode, and we switch on */
/* it to perform low-level Printing calls. */
case iPrBitsCtl: /* Send bitmap to printer. */
{
BitMap *pBitMapPtr;
Rect *pPortRectPtr;
int resolution;
pBitMapPtr = (BitMap *)(pb->lParam1);
pPortRectPtr = (Rect *)(pb->lParam2);
if (pb->lParam3 == lScreenBits)
resolution = 100;
else
if (pb->lParam3 == lPaintBits)
resolution = 75;
else
resolution = (int)(pb->lParam3);
return( printBitMap(
resolution,
*pBitMapPtr,
*pPortRectPtr)
);
}
break;
case iPrIOCtl: /* Text streaming. */
serialParameterBlock.ioParam.ioBuffer = (Ptr)pb->lParam1;
serialParameterBlock.ioParam.ioReqCount = pb->lParam2;
return(PBWrite(&serialParameterBlock, FALSE));
break;
case iPrEvtCtl: /* Screen print. */
{
BitMap *screenBitsPtr;
/* Fetch the QuickDraw global var. screenBits */
screenBitsPtr = (BitMap *)(*((Ptr *)(CurrentA5))
- (Ptr)(5*sizeof(Pattern) + sizeof(Cursor) + sizeof(BitMap)));
return( printBitMap(
currentSettings.screenDumpResolution,
*screenBitsPtr,
screenBitsPtr->bounds)
);
/* We should heed the 'pb->lParam1' being == 'lPrEvtTop' */
/* which means only the top window should be printed. */
/* Then we should remember to HideCursor() before print. */
/* Currently our bit map print cannot use .left and .right */
/* of portRect passed to it, only .top and .bottom. */
}
break;
case iPrDevCtl: /* CR, LF, FF, etc */
return(sendPrinterControl(pb->lParam1));
break;
case iFMgrCtl:
/* Font table modify request. pb->lParam1 is a
pointer to an FMInput record. (IM says we get an
FMOutputPtr here but MacsBug says we get a pointer to
FMInput. I go with MacsBug.) In addition, the
integer following pb->lParam1 contains our driver
reference number and a private byte we can use
internally. The application first does something
to determine the characteristics of fonts in our
printing GrafPort. The FontManager calls our
status routine with csParam = 8 and we give it a
"font characterization table". The FontManager
selects a font, and calls our control routine to
confirm the choice. */
{
/* Currently we do no modifications. */
}
break;
goodBye:
/* Application heap is about to be reinitialized. */
/* Currently the is nothing special to be done: */
/* All allocated memory is in the application heap */
/* and the ROM serial driver must not be closed. */
/* If you add some code here, remember to change dCtlFlags */
/* in 'driverOpen()'. */
break;
killCode:
/* Killing all pending (serial) I/O is requested. */
/* Should we be able to do it ??? Isn't all serial I/O */
/* done synchronously ??? Don't WE do it synchronously ??? */
break;
default:
break;
}
break; /* case CONTROL */
case STATUS:
switch (pb->csCode) {
/* pb->csCode gives opcode, and we switch on it to */
/* perform low-level Printing calls. */
case iFMgrCtl:
/* Font Manager font characterization table request. */
/* 'pb->lParam1' is a pointer to an area in memory */
/* in which to put a copy of the information */
/* describing our font capabilities/directions to QuickDraw. */
/* 'pb->lParam2' has an integer in its high order word */
/* which contains the device information from printing */
/* port's 'grafDevice' field. This is set from print record */
/* (print.prInfoPT.iDev and print.prStl.wDev) */
/* by high-level printing code -- there it was */
/* set by style dialog (PDEF 4). */
/* High order byte contains our driver's reference number so */
/* font manager could call our driver. Low order byte contains */
/* our resolution information which we use to select the correct */
/* table from our resources. */
{
htFontCharacterizationTable myFonts;
int resolution;
resolution = (int)(pb->lParam2 >> 16) & RES_MASK;
myFonts = (htFontCharacterizationTable)GetResource(
'FCHT',
RESID_OWNED_BY_PDEF+resolution
);
if (myFonts == nil)
return(ResError());
LoadResource(myFonts);
*((ptFontCharacterizationTable)pb->lParam1) = **myFonts;
}
break;
default:
break;
}
break; /* case STATUS */
case CLOSE:
/* Applications seldom close our driver: to do this, they should call */
/* 'PrDrvrClose' in addition to PrClose (see IM II-157). */
/* Closing the driver seems to be reserved for 'Chooser' */
/* when it changes the active printer resource file. */
/* Don't reference any driver global variables here: */
/* We don't know if they are allocated or not. */
/* In MultiFinder we cannot know if another application */
/* needs us despite another wants to close. */
/* So we don't deallocate our globals: this has the unfortunate */
/* side effect: using Chooser leaves a block of them floating. */
/***
if (dce->dCtlStorage != nil) {
(*(long *)(*dce->dCtlStorage)) = 0L; / * destroy our magic number * /
DisposHandle(dce->dCtlStorage);
dce->dCtlStorage=nil;
}
***/
break;
default:
/* Device drivers have only OPEN, PRIME, CONTROL, STATUS, and CLOSE. */
break;
} /* switch routineSelector */
/* Changing the low-memory global 'PrintErr' is reserved for high-level printing */
/* code -- don't change it here except for OPEN call. */
/* 'PrintErr' must retain its value (as set for example when writing to spool file) */
/* despite calls to low-level driver. */
return(noErr);
} /* main */
OSErr
driverOpen(dce)
DCtlPtr dce;
{
OSErr retCode;
if (openPrinterResourceFile(dce) == -1)
return(dInstErr); /* 'couldn't find driver in resource file' ??? */
/* Get driver's initialized control storage from printer resource file, */
/* lock it, and make A4 point to it thus LSC can use variables in it like globals. */
if ((dce->dCtlStorage = (Handle)GetResource('PREC', RESID_OWNED_BY_PDEF)) == nil)
return(ResError());
{
Handle storage;
Ptr pointer;
storage = dce->dCtlStorage;
LoadResource(storage);
DetachResource(storage);
HNoPurge(storage);
HLock(storage);
/* Strip off Memory Manager bits from the pointer. */
pointer = (Ptr)((long)(*storage) & (Lo3Bytes));
asm {
movea.l pointer,a4 ;; LSC uses A4 as the base for driver globals
}
}
/* Now our globals are accessible. */
/* theWorld = myWorld; ??? */
if ((retCode = reloadSettings()) != noErr)
return(retCode);
if ((retCode = openSerialDriverAndConfigureIt()) != noErr)
return(retCode);
/* High-level driver (PDEF0&5) communicates with low-level (XPrint) */
/* using this initialized global parameter block 'xPrintParameterBlock'. */
/* Now we initialize the pointer to the name of the driver: */
xPrintParameterBlock.ioNamePtr = xPrintName;
/*== #define sPrDrvr from <PrintMgr.h>, */
/* initialized in "prglobals.h". */
return(noErr);
} /* driverOpen */
int
openPrinterResourceFile(dce)
DCtlPtr dce;
/* Find the system folder and ensure our printer resource file is open. */
/* Fill in a global SysEnvRec, which might be useful for other stuff. */
{
/*** SysEnvRec myWorld; ***/
StringHandle ourname;
int theRefNum;
OSErr retCode;
/* Should not be called if we have something allocated. */
if (dce->dCtlStorage != nil)
SysError(25);
/* Find the name of our printer resource file. It is in the System file. */
if ((ourname = (StringHandle)GetResource('STR ', RESID_OWNED_BY_PDEF)) == nil)
return(-1);
LoadResource(ourname);
HLock(ourname);
/*** SysEnvirons(1, &myWorld); ***/
/*** if (myWorld.machineType >= envMachUnknown)
theRefNum = OpenRFPerm(*ourname, myWorld.sysVRefNum, fsCurPerm);
else ***/ {
int systemResFile;
/* Get the actual reference number of system file. */
{
int saveResFile;
saveResFile = CurResFile();
UseResFile(0); /* System file, IM I-117. */
systemResFile = CurResFile();
UseResFile(saveResFile);
}
{
int saveVolRefNum;
Str255 saveVolName;
int systemVolumeRefNum;
/* Save current volume refNum. */
if (GetVol(&saveVolName, &saveVolRefNum) != noErr) /* save current default volume */
saveVolRefNum = 0; /* We hope 0 is not a legal volume reference number. */
/* Get the volume where system file resides. */
theRefNum = -1;
if (GetVRefNum(systemResFile, &systemVolumeRefNum) == noErr)
if (SetVol(nil, systemVolumeRefNum) == noErr) /* open in the same folder as System */
theRefNum = OpenResFile(*ourname);
/* theRefNum == opened resource file refNum or -1 */
if (saveVolRefNum != 0)
SetVol(nil, saveVolRefNum); /* restore current default volume */
}
}
HUnlock(ourname);
HPurge(ourname);
return(theRefNum);
} /* openPrinterResourceFile */
OSErr
reloadSettings()
{
htSettings hSettings;
StringHandle hStrings;
if ((hSettings = (htSettings)GetResource('Stng',RESID_OWNED_BY_PDEF)) == nil)
return(ResError());
LoadResource(hSettings);
HNoPurge(hSettings);
currentSettings = **hSettings;
HPurge(hSettings);
if ((hStrings = (StringHandle)GetResource('STR#',RESID_OWNED_BY_CHOOSER_PACK)) == nil)
return(ResError());
LoadResource(hStrings);
HLock(hStrings);
setControlStrings(hStrings);
HUnlock(hStrings);
HPurge(hStrings);
} /* reloadSettings */
void
setControlStrings(myStrings)
StringHandle myStrings;
{
StringPtr strptr;
copyPascalString( (strptr = (*myStrings)+2) , prlfstr);
copyPascalString( (strptr += (*strptr) + 1) , prinitstr);
copyPascalString( (strptr += (*strptr) + 1) , prtopstr);
copyPascalString( (strptr += (*strptr) + 1) , preopstr);
copyPascalString( (strptr += (*strptr) + 1) , preofstr);
decodeCaretsToControlCharacters(prlfstr);
decodeCaretsToControlCharacters(prinitstr);
decodeCaretsToControlCharacters(prtopstr);
decodeCaretsToControlCharacters(preopstr);
decodeCaretsToControlCharacters(preofstr);
} /* setControlStrings */
void
copyPascalString(src, dst)
StringPtr src, dst;
/* This is for copying PASCAL strings (simple) */
{
asm {
clr.l d0
move.l src,a0
move.l dst,a1
move.b (a0),d0
loop:
move.b (a0)+,(a1)+
dbra d0,@loop
}
} /* copyPascalString */
void
decodeCaretsToControlCharacters(str)
StringPtr str;
{
register unsigned char i, count;
count = 1;
for (i=0; str[0]-i++;) {
if (str[i] == '^')
str[count] = 31 & str[++i];
else
str[count] = str[i];
count++;
}
str[0] = count - 1;
} /* decodeCaretsToControlCharacters */
OSErr
openSerialDriverAndConfigureIt()
/* Open serial driver and configure it. Use low-level ROM routines. */
{
OSErr retCode;
switch (currentSettings.port) {
case 0: /* modem port */
retCode = openDriverByName((StringPtr)"\p.AOut");
break;
case 1: /* printer port */
retCode = openDriverByName((StringPtr)"\p.BOut");
break;
}
if (retCode != noErr)
return(retCode);
/* Set up the I/O parameter block for writing to the serial driver. */
reconfigureSerialPort();
return(noErr);
} /* openSerialDriverAndConfigureIt */
OSErr
openDriverByName(name)
Str255 name;
/* Open a driver by name. ROM, Ram, serial driver? Who cares? */
{
serialParameterBlock.ioParam.ioNamePtr = name;
serialParameterBlock.ioParam.ioCompletion = nil;
serialParameterBlock.ioParam.ioPermssn = fsCurPerm;
return(PBOpen(&serialParameterBlock, FALSE));
} /* openDriverByName */
/* Set up the I/O parameter block for writing to the serial driver. */
/* A control call resets the baud rate, another the handshaking method. */
void
reconfigureSerialPort()
{
ParmBlkPtr pb;
int serialDriverRefNum;
int serialConfigWord;
switch (currentSettings.port) {
case 0: /* modem port */
serialDriverRefNum = AoutRefNum;
break;
case 1: /* printer port */
serialDriverRefNum = BoutRefNum;
break;
}
pb = &serialParameterBlock;
pb->ioParam.ioRefNum = serialDriverRefNum;
pb->ioParam.ioCompletion = nil;
((cntrlParam *)pb)->csCode = SERIAL_RESET;
serialConfigWord = data8 + noParity + stop20;
serialConfigWord += currentSettings.baud;
((cntrlParam *)pb)->csParam[0] = serialConfigWord;
(void)PBControl(pb, FALSE); /* Always returns 'noErr' according to IM II-251 */
#define shake ((SerShk*)&((cntrlParam*)pb)->csParam[0])
shake->errs = FALSE;
shake->evts = FALSE;
shake->fDTR = FALSE;
shake->fInX = FALSE;
/* if (currentSettings.XonXoff && (theWorld.machineType) >= envMachUnknown)) { ???
shake->fXOn = TRUE;
shake->fCTS = FALSE;
shake->xOn = XON_CHAR;
shake->xOff = XOFF_CHAR;
}
else { */
shake->fXOn = FALSE;
shake->fCTS = TRUE;
/* } */
((cntrlParam *)pb)->csCode = SERIAL_HANDSHAKE;
(void)PBControl(pb, FALSE); /* Always returns 'noErr' according to IM II-251 */
#undef shake
pb->ioParam.ioPosMode = 0;
pb->ioParam.ioPosOffset = 0;
} /* reconfigureSerialPort */
OSErr
sendPrinterControl(code)
long code;
/* printer device control calls */
{
switch(code) {
case lPrReset:
return(sendString(prinitstr));
case lPrPageEnd:
return(sendString(preopstr));
case lPrLineFeed:
case lPrLFSixth:
case lPrLFEighth:
return(sendString(prlfstr));
default:
return(controlErr);
}
} /* sendPrinterControl */
OSErr
sendString(string)
StringPtr string;
/* printer control string to serial driver */
{
serialParameterBlock.ioParam.ioBuffer = (Ptr)(string+1);
serialParameterBlock.ioParam.ioReqCount = (long)(string[0]);
return(PBWrite(&serialParameterBlock, FALSE));
} /* sendString */
OSErr
printBitMap(resolution, dumpBitMap, dumpPortRect)
int resolution;
BitMap dumpBitMap;
Rect dumpPortRect;
{
Str255 numAsString;
int i;
OSErr retCode;
int compressedLineLength;
unsigned char compressedBitLine[500]; /* ??? LineLength+(LineLength/128) */
/* Set bit map resolution */
switch (resolution) {
case 75:
retCode = sendString((StringPtr)"\p\033*t75R");
break;
case 100:
retCode = sendString((StringPtr)"\p\033*t100R");
break;
case 150:
retCode = sendString((StringPtr)"\p\033*t150R");
break;
case 300:
retCode = sendString((StringPtr)"\p\033*t300R");
break;
default:
retCode = controlErr;
break;
}
if (retCode != noErr)
return(retCode);
/* Start graphics */
if ((retCode = sendString((StringPtr)"\p\033*r0A")) != noErr)
return(retCode);
/*
Here we must convert the bit image from Mac into 'Compaction Graphics Mode 2'.
The mode is as follows:
a repeat count (-1 to -127) followed by data byte to repeated
or
block length (0 to 127) followed by block length + 1 data bytes
*/
/* Currently we use either uncompacted graphics mode 0 or */
/* compacted graphics mode 2. */
if (currentSettings.useOnlyPCLLevel3Features) {
retCode = sendString((StringPtr)"\p\033*b0M");
}
else {
retCode = sendString((StringPtr)"\p\033*b2M");
}
if (retCode != noErr)
return(retCode);
for (i=(dumpPortRect.top - dumpBitMap.bounds.top);
i<(dumpPortRect.bottom - dumpBitMap.bounds.top);
i++) {
Boolean bitLineContainsAtLeastOneBlackPixel;
/* compress */
compressBitLine(
dumpBitMap.rowBytes,
(StringPtr)(dumpBitMap.baseAddr + (i*(dumpBitMap.rowBytes))),
&compressedLineLength,
compressedBitLine,
&bitLineContainsAtLeastOneBlackPixel
);
if (bitLineContainsAtLeastOneBlackPixel)
/* PTT will test if LaserJet cad do positioning by 1/300": */
/*** || currentSettings.useOnlyPCLLevel3Features) ***/
{
/* Start of line */
if ((retCode = sendString((StringPtr)"\p\033*b")) != noErr)
return(retCode);
NumToString((long)compressedLineLength, numAsString);
if ((retCode = sendString(numAsString)) != noErr) /* line length */
return(retCode);
if ((retCode = sendString((StringPtr)"\pW")) != noErr)
return(retCode);
/* Send the string to printer. */
serialParameterBlock.ioParam.ioBuffer = (Ptr)compressedBitLine;
serialParameterBlock.ioParam.ioReqCount = (long)compressedLineLength;
if ((retCode = PBWrite(&serialParameterBlock, FALSE)) != noErr)
return(retCode);
}
else {
int numberOfConsecutiveEmptyBitLines;
i++;
numberOfConsecutiveEmptyBitLines = 1;
while(TRUE) {
compressBitLine(
dumpBitMap.rowBytes,
(StringPtr)(dumpBitMap.baseAddr + (i*(dumpBitMap.rowBytes))),
&compressedLineLength,
compressedBitLine,
&bitLineContainsAtLeastOneBlackPixel
);
if (bitLineContainsAtLeastOneBlackPixel)
break;
i++;
numberOfConsecutiveEmptyBitLines++;
}
/* Skip empty lines using HP DeskJet specific command. */
if ((retCode = sendString((StringPtr)"\p\033*p+")) != noErr)
return(retCode);
NumToString((long)((300/resolution) * numberOfConsecutiveEmptyBitLines), numAsString);
if ((retCode = sendString(numAsString)) != noErr) /* bit lines to skip */
return(retCode);
if ((retCode = sendString((StringPtr)"\pY")) != noErr)
return(retCode);
/* Continue processing (possible) non-empty line again. */
i--;
}
} /* for */
/* End graphics */
return(sendString((StringPtr)"\p\033*rB"));
} /* printBitMap */
void
compressBitLine(lineLength, bitLine, compressedLineLength, compressedBitLine, dirty)
int lineLength;
StringPtr bitLine;
int *compressedLineLength;
StringPtr compressedBitLine;
Boolean *dirty;
{
int i, k, m, state;
Str255 String;
if (currentSettings.useOnlyPCLLevel3Features) {
BlockMove(bitLine, compressedBitLine, (Size)lineLength);
*compressedLineLength = lineLength; /* length does not change */
*dirty = FALSE;
for (i=0;i<lineLength;i++)
if (bitLine[i] != (unsigned char)0)
*dirty = TRUE;
return;
}
#define INITIAL 0
#define SAME 1
#define DIFFERENT 2
#ifdef DEBUG_COMPRESSING_STATE_MACHINE
sendString((StringPtr)"\p\n\rcompressString(lineLength=");
NumToString((long)lineLength, String);
sendString(String);
sendString((StringPtr)"\p, bitLine=");
NumToString((long)(bitLine), String);
sendString(String);
sendString((StringPtr)"\p, compressedLineLength=");
NumToString((long)(compressedLineLength), String);
sendString(String);
sendString((StringPtr)"\p, compressedBitLine=");
NumToString((long)(compressedBitLine), String);
sendString(String);
sendString((StringPtr)"\p);\n\r");
#endif DEBUG_COMPRESSING_STATE_MACHINE
i = 0; /* input buffer */
k = 0; /* output buffer */
m = 0; /* number of bytes in a group (max. 128) */
state = INITIAL;
*dirty = FALSE;
while (TRUE) {
if (bitLine[i] != (unsigned char)0)
*dirty = TRUE;
#ifdef DEBUG_COMPRESSING_STATE_MACHINE
sendString((StringPtr)"\pstate=");
NumToString((long)state, String);
sendString(String);
sendString((StringPtr)"\p, i=");
NumToString((long)i, String);
sendString(String);
sendString((StringPtr)"\p, bitLine[i]=");
NumToString((long)(bitLine[i]), String);
sendString(String);
sendString((StringPtr)"\p, bitLine[i+1]=");
NumToString((long)(bitLine[i+1]), String);
sendString(String);
sendString((StringPtr)"\p, k=");
NumToString((long)k, String);
sendString(String);
sendString((StringPtr)"\p, m=");
NumToString((long)m, String);
sendString(String);
sendString((StringPtr)"\p\n\r");
#endif DEBUG_COMPRESSING_STATE_MACHINE
switch (state) {
case INITIAL: {
if (i >= lineLength)
goto outOfWhile; /* input buffer finished */
if (i == (lineLength-1)) {
compressedBitLine[k++] = 0; /* 1 different byte */
compressedBitLine[k++] = bitLine[i];
goto outOfWhile;
}
if (bitLine[i] == bitLine[i+1]) {
state = SAME;
}
else {
state = DIFFERENT;
}
break;
} /* case INITIAL */
case SAME: {
if (i >= lineLength)
goto outOfWhile; /* input buffer finished */
if (i == (lineLength-1)) { /* ends with at least 2 same bytes */
compressedBitLine[k++] = (unsigned char)(- m); /* repeat count: 2 -> -1, 3 -> -2 etc. */
compressedBitLine[k++] = bitLine[i];
goto outOfWhile; /* input buffer finished */
}
if (m >= 126) {
compressedBitLine[k++] = (unsigned char)(- m); /* repeat count: 2 -> -1, 3 -> -2 etc. */
compressedBitLine[k++] = bitLine[i]; /* last same byte of this group */
i++; /* the first byte of next group (same/different, don't know yet) */
m = 0;
state = INITIAL;
break;
}
if (bitLine[i] == bitLine[i+1]) {
i++;
m++; /* Yippee, one more! */
state = SAME;
break;
}
else {
compressedBitLine[k++] = (unsigned char)(- m); /* repeat count: 2 -> -1, 3 -> -2 etc. */
compressedBitLine[k++] = bitLine[i]; /* last same byte of this group */
i++; /* the first byte of next group (same/different, don't know yet) */
m = 0;
state = INITIAL;
break;
}
break;
} /* case SAME */
case DIFFERENT: {
if (i >= lineLength)
goto outOfWhile; /* input buffer finished */
if (i == (lineLength-1)) { /* line ends with at least two different bytes */
i -= m;
compressedBitLine[k++] = (unsigned char)(m); /* char count: 2 -> 1, 3 -> 2 etc. */
for (;m>=0;m--)
compressedBitLine[k++] = bitLine[i++];
goto outOfWhile; /* input buffer finished */
}
if (m >= 126) {
i -= m;
compressedBitLine[k++] = (unsigned char)(m); /* char count: 2 -> 1, 3 -> 2 etc. */
for (;m>=0;m--)
compressedBitLine[k++] = bitLine[i++];
m = 0;
state = INITIAL;
break;
}
if (bitLine[i] != bitLine[i+1]) {
i++;
m++; /* Yippee, one more! */
state = DIFFERENT;
break;
}
else {
i -= m;
compressedBitLine[k++] = (unsigned char)(m); /* char count: 2 -> 1, 3 -> 2 etc. */
for (;m>=0;m--)
compressedBitLine[k++] = bitLine[i++];
m = 0;
state = INITIAL;
break;
}
break;
} /* case DIFFERENT */
} /* switch */
} /* while */
outOfWhile:
*compressedLineLength = k;
} /* compressBitLine */